/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package locusts.server;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import locusts.common.Player;
import org.apache.mina.common.ByteBuffer;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.IoHandlerAdapter;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.SimpleByteBufferAllocator;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;

/**
 *
 * @author hamish
 */
public class Server extends IoHandlerAdapter implements
        Game.Listener {

    //
    private final Game game;
    private final IoAcceptor acceptor;
    private final SocketAcceptorConfig saConfig;
    private final Map<IoSession, Player> sessions;
    private final ServerConfig svConfig;

    public Server(Game game, ServerConfig svConfig) throws IOException {
        this.svConfig = svConfig;
        this.game = game;
        this.game.addGameListener(this);
        sessions = Collections.synchronizedMap(
                new HashMap<IoSession, Player>());
        ByteBuffer.setUseDirectBuffers(false);
        ByteBuffer.setAllocator(new SimpleByteBufferAllocator());

        acceptor = new SocketAcceptor();
        saConfig = new SocketAcceptorConfig();
        saConfig.getFilterChain().addLast("codec", new ProtocolCodecFilter(
                new ObjectSerializationCodecFactory()));
    }

    public void start() throws IOException {
        acceptor.bind(svConfig.getSocketAddress(), this, saConfig);
        System.out.println("Mouse Tag server started.");
    }

    public void stop() {
        acceptor.unbindAll();
        System.out.println("Mouse Tag server stopped.");
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        session.close();
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        session.write(game.getConfig());
        session.write(game.getWorld());
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        if (sessions.containsKey(session)) {
            game.removePlayer(sessions.get(session));
            sessions.remove(session);
        }
    }

    @Override
    public void messageReceived(IoSession session, Object message)
            throws Exception {
        if (message instanceof Player) {
            Player p = (Player) message;
            if (game.playerExists(p.getName()))
                game.updatePlayer(p);
            else {
                sessions.put(session, p);
                game.addPlayer(p);
            }
        } else {
            System.out.println("Unknown message type: " + message.
                    getClass());
        }
    }

    private void broadcast(Object message) {
        for (IoSession se : sessions.keySet()) {
            se.write(message);
        }
    }

    public void gameUpdate(Game game) {
        broadcast(game.getWorld());
    }
}
